use base64::Engine;
use anyhow::anyhow;

pub const KEY_LEN: usize = 32;

#[derive(Clone)]
pub struct Key([u8; KEY_LEN]);

impl TryFrom<&[u8]> for Key {
    type Error = anyhow::Error;
    fn try_from(v: &[u8]) -> Result<Key, Self::Error> {
        if v.len() != KEY_LEN {
            return Err(anyhow!("密钥长度错误"));
        }
        let mut k = [0; KEY_LEN];
        k.copy_from_slice(v);
        Ok(Key(k))
    }
}

impl From<[u8; KEY_LEN]> for Key {
    fn from(value: [u8; KEY_LEN]) -> Self {
        Self(value)
    }
}

impl Key {
    pub fn as_slice(&self) -> &[u8] {
        self.0.as_ref()
    }

    pub fn to_bytes(&self) -> [u8; KEY_LEN] {
        self.0
    }

    pub fn to_base64(&self) -> String {
        base64::prelude::BASE64_STANDARD.encode(self.as_slice())
    }

    pub fn try_from_base64(s: &str) -> Result<Self, anyhow::Error> {
        let v = base64::prelude::BASE64_STANDARD.decode(s)?;
        Key::try_from(v.as_slice())
    }
}

#[derive(Clone)]
pub struct PrivateKey(Key);

impl PrivateKey {
    pub fn random() -> Self {
        let k = x25519_dalek::StaticSecret::random();
        PrivateKey(Key(k.to_bytes()))
    }

    pub fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }

    pub fn to_bytes(&self) -> [u8; KEY_LEN] {
        self.0.to_bytes()
    }

    pub fn to_base64(&self) -> String {
        self.0.to_base64()
    }

    pub fn try_from_base64(s: &str) -> Result<Self, anyhow::Error> {
        let k = Key::try_from_base64(s)?;
        Ok(PrivateKey(k))
    }
}

impl TryFrom<&[u8]> for PrivateKey {
    type Error = anyhow::Error;

    fn try_from(v: &[u8]) -> Result<PrivateKey, Self::Error> {
        let key = Key::try_from(v)?;
        Ok(PrivateKey(key))
    }
}

impl From<[u8; KEY_LEN]> for PrivateKey {
    fn from(value: [u8; KEY_LEN]) -> Self {
        PrivateKey(Key::from(value))
    }
}

#[derive(Clone)]
pub struct PublicKey(Key);

impl PublicKey {
    pub fn as_slice(&self) -> &[u8] {
        self.0.as_slice()
    }

    pub fn to_bytes(&self) -> [u8; KEY_LEN] {
        self.0.to_bytes()
    }

    pub fn to_base64(&self) -> String {
        self.0.to_base64()
    }

    pub fn try_from_base64(s: &str) -> Result<Self, anyhow::Error> {
        let k = Key::try_from_base64(s)?;
        Ok(PublicKey(k))
    }
}

impl From<&PrivateKey> for PublicKey {
    fn from(v: &PrivateKey) -> Self {
        let k = x25519_dalek::StaticSecret::from(v.to_bytes());
        let k = x25519_dalek::PublicKey::from(&k);
        PublicKey(Key(k.to_bytes()))
    }
}

impl TryFrom<&[u8]> for PublicKey {
    type Error = anyhow::Error;

    fn try_from(v: &[u8]) -> Result<PublicKey, Self::Error> {
        let key = Key::try_from(v)?;
        Ok(PublicKey(key))
    }

}